package org.dsa.iot.dslink.provider.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.CharsetUtil;
import org.dsa.iot.dslink.provider.HttpProvider;
import org.dsa.iot.dslink.util.URLInfo;
import org.dsa.iot.dslink.util.http.HttpResp;
import org.dsa.iot.shared.SharedObjects;
import javax.net.ssl.TrustManagerFactory;
import java.util.Map;
/**
* @author Samuel Grenier
*/
public class DefaultHttpProvider extends HttpProvider {
@Override
public HttpResp post(URLInfo url,
byte[] content,
Map<String, String> headers) {
if (url == null) {
throw new NullPointerException("url");
}
try {
final HttpHandler handler = new HttpHandler();
Bootstrap b = new Bootstrap();
b.group(SharedObjects.getLoop());
b.channel(NioSocketChannel.class);
b.handler(new Initializer(handler, url.secure));
ChannelFuture fut = b.connect(url.host, url.port);
Channel chan = fut.sync().channel();
chan.writeAndFlush(populateRequest(url.path, content, headers));
fut.channel().closeFuture().sync();
return populateResponse(handler);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private HttpRequest populateRequest(String uri,
byte[] content,
Map<String, String> headers) {
DefaultFullHttpRequest request = new DefaultFullHttpRequest(
HttpVersion.HTTP_1_1, HttpMethod.POST, uri);
ByteBuf buf = request.content();
if (content != null) {
buf.writeBytes(content);
}
{
HttpHeaders h = request.headers();
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
h.set(name, value);
}
}
String len = String.valueOf(buf.readableBytes());
h.set(HttpHeaderNames.CONTENT_LENGTH, len);
}
return request;
}
private HttpResp populateResponse(HttpHandler handler) throws Throwable {
Throwable throwable = handler.getThrowable();
if (throwable != null) {
throw throwable;
}
HttpResp resp = new HttpResp();
resp.setStatus(handler.getStatus());
resp.setBody(handler.getContent());
return resp;
}
private static class Initializer extends ChannelInitializer<SocketChannel> {
private final HttpHandler handler;
private final boolean secure;
public Initializer(HttpHandler handler, boolean secure) {
this.handler = handler;
this.secure = secure;
}
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
if (secure) {
TrustManagerFactory man = InsecureTrustManagerFactory.INSTANCE;
SslContextBuilder scb = SslContextBuilder.forClient();
SslContext con = scb.trustManager(man).build();
p.addLast(con.newHandler(ch.alloc()));
}
p.addLast(new HttpClientCodec());
p.addLast(new HttpContentDecompressor());
p.addLast(handler);
}
}
private static class HttpHandler extends SimpleChannelInboundHandler<Object> {
private StringBuffer content = new StringBuffer();
private HttpResponseStatus status;
private Throwable t;
@Override
protected void channelRead0(ChannelHandlerContext ctx,
Object msg) throws Exception {
if (msg instanceof HttpResponse) {
HttpResponse resp = (HttpResponse) msg;
status = resp.status();
}
if (msg instanceof HttpContent) {
ByteBuf buf = ((HttpContent) msg).content();
content.append(buf.toString(CharsetUtil.UTF_8));
}
if (msg instanceof LastHttpContent) {
ctx.close();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable t) {
this.t = t;
ctx.close();
}
public Throwable getThrowable() {
return t;
}
public HttpResponseStatus getStatus() {
return status;
}
public String getContent() {
return content.toString();
}
}
}